package ptij.ADSL.de.ATUC;

import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.data.*;
import ptolemy.data.expr.*;
import ptolemy.data.type.*;
import ptolemy.actor.*;

import ptij.ADSL.common.MuxDataFrame;

//////////////////////////////////////////////////////////////////////////
//// ScramblerTx
/**

@author Ivan Jeukens
*/
public class ScramblerTx extends TypedAtomicActor {    

    public ScramblerTx(CompositeEntity container, String name) throws
            NameDuplicationException, IllegalActionException {
        super(container, name);
	
	input = new TypedIOPort(this, "input", true, false);
	input.setTypeEquals(BaseType.OBJECT);
	
	output = new TypedIOPort(this, "output", false, true);
	output.setTypeEquals(BaseType.OBJECT);

	AS0Bytes = new Parameter(this, "AS0Bytes", new IntToken(8));
	LS0Bytes = new Parameter(this, "LS0Bytes", new IntToken(8));
	
	AS0Map = new Parameter(this, "AS0Map", new BooleanToken(false));
	LS0Map = new Parameter(this, "LS0Map", new BooleanToken(false));
	
	S = new Parameter(this, "S", new IntToken(1));
    }

    ///////////////////////////////////////////////////////////////////
    ////                     ports and parameters                  ////
    
    public TypedIOPort input;
    public TypedIOPort output;

    public Parameter AS0Bytes;
    public Parameter LS0Bytes;
    
    public Parameter AS0Map;
    public Parameter LS0Map;
    
    public Parameter S;

    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    public void attributeChanged(Attribute attribute)
            throws IllegalActionException {
        if(attribute == AS0Bytes) {
	    IntToken it = (IntToken) AS0Bytes.getToken();
	    _AS0Bytes = it.intValue();            
        }
        else
        if(attribute == LS0Bytes) {
            IntToken it = (IntToken) LS0Bytes.getToken();
            _LS0Bytes = it.intValue();            
        }
        else
        if(attribute == AS0Map) {
            BooleanToken bt = (BooleanToken) AS0Map.getToken();
	    _AS0Map = bt.booleanValue();
        }
        else
        if(attribute == LS0Map) {
            BooleanToken bt = (BooleanToken) LS0Map.getToken();
	    _LS0Map = bt.booleanValue();   
        }
        else
        if(attribute == S) {
 	    IntToken it = (IntToken) S.getToken();
	    _S = it.intValue();           
        }
	else {
            super.attributeChanged(attribute);
        }                
    }

    public void initialize() throws IllegalActionException {
        super.initialize();	 	
	
        _checkParameters();
        
        _fastReg = 0;
	_interReg = 0;
	_frameCounter = 0;		
    }
    
    public void fire() throws IllegalActionException {    
        ObjectToken ot = (ObjectToken) input.get(0);
	MuxDataFrame mf = (MuxDataFrame) ot.getValue();
        int b;
	
	if(_frameCounter == 0) {
	    b = _processFast(mf.getFastByte());
	    mf.setFastByte(b);
	    
	    if(!_AS0Map) {
	        for(int i = 0;i < _AS0Bytes;i++) {
		    b = _processFast(mf.getAS0Byte(i));
		    mf.setAS0Byte(b, i);
		}
	    }
	    
	    if(!_LS0Map) {
	        for(int i = 0;i < _LS0Bytes;i++) {
		    b = _processFast(mf.getLS0Byte(i));
		    mf.setLS0Byte(b, i);
		}
	    }
	    
	    if(mf.hasAEX()) {
	        b = _processFast(mf.getAEXByte());
		mf.setAEXByte(b);
	    }
	    
	    if(mf.hasLEX()) {
	        b = _processFast(mf.getLEXByte());
		mf.setLEXByte(b);
	    }
	}
	else {
	    b = _processInter(mf.getFastByte());
	    mf.setFastByte(b);
	    
	    if(_AS0Map) {
	        for(int i = 0;i < _AS0Bytes;i++) {
		    b = _processInter(mf.getAS0Byte(i));
		    mf.setAS0Byte(b, i);
		}
	    }
	    
	    if(_LS0Map) {
	        for(int i = 0;i < _LS0Bytes;i++) {
		    b = _processInter(mf.getLS0Byte(i));
		    mf.setLS0Byte(b, i);
		}
	    }
	    
	    if(mf.hasAEX()) {
	        b = _processInter(mf.getAEXByte());
		mf.setAEXByte(b);
	    }
	    
	    if(mf.hasLEX()) {
	        b = _processInter(mf.getLEXByte());
		mf.setLEXByte(b);
	    }
	}
	_frameCounter++;
	if(_frameCounter == 1 + _S) _frameCounter = 0;

        ot = new ObjectToken(mf);
	output.broadcast(ot);
    }

    ///////////////////////////////////////////////////////////////////
    ////                         private methods                   ////
       
    private int _processFast(int b) {
        int ret = 0;
	int b1, b2, b3;
	
	for(int i = 0;i < 8;i++) {
	    b1 = (_fastReg >> 23) & 0x01;
	    b2 = (_fastReg >> 18) & 0x01;
	    b3 = (b >> i) & 0x01;
	    ret |= ((b1 ^ b2 ^ b3) << i);
	    _fastReg = (_fastReg << 1) | (b1 ^ b2 ^ b3);
	}	
	return ret;
    } 
    
    private int _processInter(int b) {
        int ret = 0;
	int b1, b2, b3;
	
	for(int i = 0;i < 8;i++) {
	    b1 = (_interReg >> 23) & 0x01;
	    b2 = (_interReg >> 18) & 0x01;
	    b3 = (b >> i) & 0x01;	    
	    ret |= ((b1 ^ b2 ^ b3) << i);
	    _interReg = (_interReg << 1) | (b1 ^ b2 ^ b3);
	}	
	return ret;
    } 
    
    private void _checkParameters() throws IllegalActionException {    
        if(_AS0Bytes < 0) {
	    throw new IllegalActionException(this, "AS0 Bytes < 0");	
	}
	
	if(_LS0Bytes < 0) {
	    throw new IllegalActionException(this, "LS0 Bytes < 0");
	}
	
	if(_S != 1 && _S != 2 && _S != 4 && _S != 8 && _S != 16) {
	    throw new IllegalActionException(this, "\nInvalid value(" +
	        _S + ") for parameter S");
	}
    }            

    ///////////////////////////////////////////////////////////////////
    ////                         private variables                 ////
    
    private int _fastReg;
    private int _interReg;

    private int _frameCounter;

    private int _AS0Bytes;
    private int _LS0Bytes;
    private boolean _AS0Map;
    private boolean _LS0Map;
    private int _S;
}
